﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using CleverDD.Model.Communication.PLC;
using HslCommunication;
using HslCommunication.Profinet.Siemens;

namespace CleverDD.Helper.Communication.PLC
{
    /// <summary>
    /// 西门子PLC
    /// </summary>
    public class SiemensBasePlc : BasePlcReadAndWrite
    {
        private SiemensS7Net _plc;
        private byte _rack, _slot;


        #region 单例模式  取消单例模式就将构造函数的private改成public
        private static object _o = new object();

        // 1. 构造函数私有化
        public SiemensBasePlc(PlcType plcType)
        {
            HslCommunication.Authorization.SetAuthorizationCode("b23b00e2-ce46-4bfc-b33c-71c47c2c11c2");

            SiemensPLCS siemensPlcSelected = SiemensPLCS.S1200;
            switch (plcType)
            {
                case PlcType.西门子1200:
                    _rack = 0;
                    _slot = 0;
                    siemensPlcSelected = SiemensPLCS.S1200;
                    break;
                case PlcType.西门子1500:
                    _rack = 0;
                    _slot = 0;
                    siemensPlcSelected = SiemensPLCS.S1500;
                    break;
                case PlcType.西门子300:
                    _rack = 0;
                    _slot = 2;
                    siemensPlcSelected = SiemensPLCS.S300;
                    break;
                case PlcType.西门子400:
                    _rack = 0;
                    _slot = 3;
                    siemensPlcSelected = SiemensPLCS.S400;
                    break;
                case PlcType.西门子200Smart:
                    _rack = 0;
                    _slot = 0;
                    siemensPlcSelected = SiemensPLCS.S200Smart;
                    break;
            }
            _plc = new SiemensS7Net(siemensPlcSelected);

        }

        // 2. 写一个私有的字段 volatile避免重排的问题
        private static volatile SiemensBasePlc _siemensPlc;

        // 3. 写一个方法暴露给外界这个实例
        public static SiemensBasePlc GetSiemensPlc(PlcType plcType)
        {
            // lock是一个互斥锁，Monitor.Enter() Monitor.Exit()
            if (_siemensPlc == null)
            {
                lock (_o)
                {
                    if (_siemensPlc == null)
                    {
                        // 1. 开辟一块内存空间
                        // 2. 创建一个对象
                        // 3. 将对象指向内存空间
                        // 123  132 指针重排
                        _siemensPlc = new SiemensBasePlc(plcType);
                    }
                }
            }
            return _siemensPlc;
        }


        #endregion


        #region 打开连接

        public override OperateResult ConnPlc(string ip, int port)
        {
            OperateResult operateResult = new OperateResult();
            if (!IPAddress.TryParse(ip, out _)) 
            {
                operateResult.IsSuccess = false;
                operateResult.Message = "IP 解析失败";
                return operateResult;
            }
            //设置读取到数据的高低位
            // _plc.ByteTransform.IsStringReverseByteWord = true;
            //_plc.ByteTransform.DataFormat = HslCommunication.Core.DataFormat.DCBA;
            //_plc.ByteTransform.DataFormat = HslCommunication.Core.DataFormat.ABCD;

            _plc.IpAddress = ip;
            _plc.Port = port;
            try
            {
                _plc.Rack = _rack;
                _plc.Slot = _slot;
                var connect = _plc.ConnectServer();
                IsConnected = connect.IsSuccess;

                return connect;
            }
            catch (Exception ex)
            {
                operateResult.IsSuccess = false;
                operateResult.Message = ex.Message;
                return operateResult;
            }
        }

        #endregion

        #region 直接Copy的方法

        #region 关闭连接
        public override OperateResult ClosePlc()
        {
            try
            {
                OperateResult result = _plc.ConnectClose();
                if (result.IsSuccess)
                {
                    IsConnected = false;
                }

                return result;
            }
            catch (Exception ex)
            {
                OperateResult operateResult = new OperateResult();
                operateResult.IsSuccess = false;
                operateResult.Message = ex.Message;
                return operateResult;
            }
        }
        #endregion

        #region 读取PLC

        public override OperateResult ReadAll(List<PlcAddressInfoEntity> plcAddressInfoList)
        {

            OperateResult operateResult = new OperateResult();
            try
            {
                foreach (var plcAddressInfo in plcAddressInfoList)
                {
                    if (plcAddressInfo.State == PlcState.默认值)
                    {
                        plcAddressInfo.Value = plcAddressInfo.DefaultValue;
                        plcAddressInfo.ReadSuccess = false;
                        continue;
                    }

                    if (plcAddressInfo.State == PlcState.停用)
                    {
                        plcAddressInfo.Value = "未启用";
                        plcAddressInfo.ReadSuccess = false;
                        continue;
                    }

                    if (string.IsNullOrEmpty(plcAddressInfo.Address))
                    {
                        plcAddressInfo.Value = "地址未设置";
                        plcAddressInfo.ReadSuccess = false;
                        continue;
                    }

                    switch (plcAddressInfo.DataType)
                    {
                        case PlcDataType.Bool1:
                            var operateResultBool = ReadBool(plcAddressInfo.Address);
                            if (operateResultBool.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultBool.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultBool.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.Byte8:
                        case PlcDataType.Short16:
                            var operateResultShort = ReadShort(plcAddressInfo.Address);
                            if (operateResultShort.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultShort.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultShort.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.UShort16:
                            var operateResultUshort = ReaduShort(plcAddressInfo.Address);
                            if (operateResultUshort.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultUshort.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultUshort.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.Int32:
                            var operateResultInt = ReadInt(plcAddressInfo.Address);
                            if (operateResultInt.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultInt.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultInt.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.UInt32:
                            var operateResultUint = ReaduInt(plcAddressInfo.Address);
                            if (operateResultUint.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultUint.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultUint.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.Long64:
                            var operateResultLong = ReadLong(plcAddressInfo.Address);
                            if (operateResultLong.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultLong.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultLong.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.ULong64:
                            var operateResultUlong = ReaduLong(plcAddressInfo.Address);
                            if (operateResultUlong.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultUlong.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultUlong.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.Float单精度:
                            var operateResultFloat = ReadFloat(plcAddressInfo.Address);
                            if (operateResultFloat.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultFloat.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultFloat.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;

                        case PlcDataType.Doubles双精度:
                            var operateResultDouble = ReadDouble(plcAddressInfo.Address);
                            if (operateResultDouble.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultDouble.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultDouble.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;
                        case PlcDataType.String字符串:
                            OperateResult<string> operateResultString = ReadString(plcAddressInfo.Address,
                                (ushort)plcAddressInfo.Length, plcAddressInfo.EncodingType);
                            if (operateResultString.IsSuccess)
                            {
                                plcAddressInfo.Value = operateResultString.Content + "";
                                plcAddressInfo.ReadSuccess = true;
                            }
                            else
                            {
                                plcAddressInfo.Value = operateResultString.Message;
                                plcAddressInfo.ReadSuccess = false;
                            }

                            break;
                        default:
                            plcAddressInfo.ReadSuccess = false;
                            plcAddressInfo.Value = "数据类型错误";
                            break;
                    }
                }

                operateResult.IsSuccess = true;
                return operateResult;
            }
            catch (Exception ex)
            {
                operateResult.IsSuccess = false;
                operateResult.Message = ex.Message;
                return operateResult;
            }

        }

        public override OperateResult<bool> ReadBool(string address)
        {
            var operateResult = _plc.ReadBool(address);
            return operateResult;
        }


        public override OperateResult<short> ReadShort(string address)
        {
            var operateResult = _plc.ReadInt16(address);
            return operateResult;
        }

        public override OperateResult<ushort> ReaduShort(string address)
        {
            var operateResult = _plc.ReadUInt16(address);
            return operateResult;
        }


        public override OperateResult<int> ReadInt(string address)
        {
            var operateResult = _plc.ReadInt32(address);
            return operateResult;
        }

        public override OperateResult<uint> ReaduInt(string address)
        {
            var operateResult = _plc.ReadUInt32(address);
            return operateResult;
        }


        public override OperateResult<long> ReadLong(string address)
        {
            var operateResult = _plc.ReadInt64(address);
            return operateResult;
        }


        public override OperateResult<ulong> ReaduLong(string address)
        {
            var operateResult = _plc.ReadUInt64(address);
            return operateResult;
        }

        public override OperateResult<float> ReadFloat(string address)
        {
            var operateResult = _plc.ReadFloat(address);
            return operateResult;
        }


        public override OperateResult<double> ReadDouble(string address)
        {
            var operateResult = _plc.ReadDouble(address);
            return operateResult;
        }

        /// <summary>
        ///     1.读取字符串的方法，西门子PLC不需要长度，三菱的必须要
        ///     2.这个方法的长度应该是最大长度，猜测的
        ///     3.因为要长度，没有开始两位代表字节长度，所以会有后面补全的情况
        /// </summary>
        /// <param name="address"></param>
        /// <param name="length"></param>
        /// <param name="encodingType"></param>
        /// <returns></returns>
        public override OperateResult<string> ReadString(string address, ushort length = 0, EncodingType encodingType = EncodingType.None)
        {

            OperateResult<string> operateResult;
            switch (encodingType)
            {
                case EncodingType.None:
                    operateResult = _plc.ReadString(address, length);
                    break;
                case EncodingType.Default:
                case EncodingType.Ansi:
                case EncodingType.Gb2312:
                    operateResult = _plc.ReadString(address, length, Encoding.Default);
                    break;
                case EncodingType.Utf8:
                    operateResult = _plc.ReadString(address, length, Encoding.UTF8);
                    break;
                case EncodingType.Ascii:
                    operateResult = _plc.ReadString(address, length, Encoding.ASCII);
                    break;
                case EncodingType.Unicode:
                    operateResult = _plc.ReadString(address, length, Encoding.Unicode);
                    break;
                case EncodingType.BigEndianUnicode:
                    operateResult = _plc.ReadString(address, length, Encoding.BigEndianUnicode);
                    break;
                case EncodingType.Utf32:
                    operateResult = _plc.ReadString(address, length, Encoding.UTF32);
                    break;

                default:
                    operateResult = _plc.ReadString(address, length);
                    break;
            }

            //不足位会补全 字符串的结束符(即\0)
            //operateResult.Content 有时为null，有时为\0，有时为"",需注意
            if (!string.IsNullOrEmpty(operateResult.Content))
                operateResult.Content = operateResult.Content.Replace("\0", "").Trim();
            return operateResult;
        }

        #endregion

        #region 写入PLC

        public override OperateResult WritePlc(PlcAddressInfoEntity plcAddressInfo, string value)
        {
            try
            {
                switch (plcAddressInfo.DataType)
                {
                    case PlcDataType.Bool1:
                        return WriteBool(plcAddressInfo.Address, Convert.ToBoolean(value));
                    case PlcDataType.Byte8:
                        return WriteByte(plcAddressInfo.Address, Convert.ToByte(value));
                    case PlcDataType.Short16:
                        return WriteShort(plcAddressInfo.Address, Convert.ToInt16(value));
                    case PlcDataType.UShort16:
                        return WriteuShort(plcAddressInfo.Address, Convert.ToUInt16(value));
                    case PlcDataType.Int32:
                        return WriteInt(plcAddressInfo.Address, Convert.ToInt32(value));
                    case PlcDataType.UInt32:
                        return WriteuInt(plcAddressInfo.Address, Convert.ToUInt32(value));
                    case PlcDataType.Long64:
                        return WriteLong(plcAddressInfo.Address, Convert.ToInt64(value));
                    case PlcDataType.ULong64:
                        return WriteuLong(plcAddressInfo.Address, Convert.ToUInt64(value));
                    case PlcDataType.Float单精度:
                        return WriteFloat(plcAddressInfo.Address, Convert.ToSingle(value));
                    case PlcDataType.Doubles双精度:
                        return WriteDouble(plcAddressInfo.Address, Convert.ToDouble(value));
                    case PlcDataType.String字符串:
                        return WriteString(plcAddressInfo.Address, value, plcAddressInfo.Length, plcAddressInfo.EncodingType);
                    default:
                        OperateResult operateResult = new OperateResult();
                        operateResult.IsSuccess = false;
                        operateResult.Message = "数据类型不存在";
                        return operateResult;
                }
            }
            catch (Exception ex)
            {
                OperateResult operateResult = new OperateResult();
                operateResult.IsSuccess = false;
                operateResult.Message = ex.Message;
                return operateResult;
            }
        }


        public override OperateResult WriteBool(string address, bool value)
        {
            return _plc.Write(address, value);
        }


        public override OperateResult WriteByte(string address, byte value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteShort(string address, short value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteuShort(string address, ushort value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteInt(string address, int value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteuInt(string address, uint value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteLong(string address, long value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteuLong(string address, ulong value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteFloat(string address, float value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteDouble(string address, double value)
        {
            return _plc.Write(address, value);
        }

        public override OperateResult WriteString(string address, string value, int length = 0, EncodingType encodingType = EncodingType.None)
        {
            OperateResult operateResult;
            switch (encodingType)
            {
                case EncodingType.None:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value) :
                        _plc.Write(address, value, length);
                    break;
                case EncodingType.Default:
                case EncodingType.Ansi:
                case EncodingType.Gb2312:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.Default) :
                        _plc.Write(address, value, length, Encoding.Default);
                    break;
                case EncodingType.Utf8:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.UTF8) :
                        _plc.Write(address, value, length, Encoding.UTF8);
                    break;
                case EncodingType.Ascii:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.ASCII) :
                        _plc.Write(address, value, length, Encoding.ASCII);
                    break;
                case EncodingType.Unicode:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.Unicode) :
                        _plc.Write(address, value, length, Encoding.Unicode);
                    break;
                case EncodingType.BigEndianUnicode:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.BigEndianUnicode) :
                        _plc.Write(address, value, length, Encoding.BigEndianUnicode);
                    break;
                case EncodingType.Utf32:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value, Encoding.UTF32) :
                        _plc.Write(address, value, length, Encoding.UTF32);
                    break;
                default:
                    operateResult = length <= 0 ?
                        _plc.Write(address, value) :
                        _plc.Write(address, value, length);
                    break;
            }
            return operateResult;
        }

        #endregion


        #endregion



    }
}